/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
/*
 * This copy of Woodstox XML processor is licensed under the
 * Apache (Software) License, version 2.0 ("the License").
 * See the License for details about distribution rights, and the
 * specific rights regarding derivate works.
 *
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing Woodstox, in file "ASL2.0", under the same directory
 * as this file.
 */
package com.je.bpm.engine.impl.cmd;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.je.bpm.common.operation.OperatorEnum;
import com.je.bpm.core.model.BpmnModel;
import com.je.bpm.core.model.FlowElement;
import com.je.bpm.core.model.SequenceFlow;
import com.je.bpm.core.model.config.CustomEvent;
import com.je.bpm.core.model.gateway.Gateway;
import com.je.bpm.core.model.task.*;
import com.je.bpm.engine.ActivitiException;
import com.je.bpm.engine.ActivitiObjectNotFoundException;
import com.je.bpm.engine.RepositoryService;
import com.je.bpm.engine.impl.bpmn.behavior.KaiteBaseUserTaskActivityBehavior;
import com.je.bpm.engine.impl.context.Context;
import com.je.bpm.engine.impl.identity.Authentication;
import com.je.bpm.engine.impl.interceptor.CommandContext;
import com.je.bpm.engine.impl.persistence.entity.TaskEntity;
import com.je.bpm.engine.task.Comment;
import com.je.bpm.engine.task.Task;
import com.je.bpm.engine.upcoming.UpcomingCommentInfoDTO;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 完成任务命令定义
 */
public class CompleteTaskCmd extends AbstractCompleteTaskCmd {

    private static final long serialVersionUID = 1L;
    protected Map<String, Object> variables;
    protected Map<String, Object> transientVariables;
    protected Map<String, Object> taskVariables;
    /**
     * 当前节点处理人
     */
    public static final String PREV_ASSIGNEE = "prevAssignee";
    public static final String COUNTERSIGN_PREV_ASSIGNEE = "countersignPrevAssignee";
    /**
     * 下个节点指派人
     */
    protected String assigneeUser;
    /**
     * 外出方向
     */
    protected String target;
    /**
     * 是否本任务作用域
     */
    protected boolean localScope;
    protected String comment;
    protected String sequentials;
    protected String isJump;
    protected boolean countersign = false;
    protected boolean signBack = false;
    protected Object histVar = null;

    public CompleteTaskCmd(String taskId, Map<String, Object> variables) {
        super(taskId, null, null);
        this.variables = variables;
    }

    public CompleteTaskCmd(String taskId, String assigneeUser, String target, String prod,
                           Map<String, Object> bean, String comment, String sequentials, String isJump) {
        super(taskId, prod, bean);
        this.assigneeUser = assigneeUser;
        this.target = target;
        this.comment = comment;
        this.sequentials = sequentials;
        this.isJump = isJump;
    }

    public CompleteTaskCmd(String taskId, String assigneeUser, String target, String prod,
                           Map<String, Object> bean, String comment, String sequentials, String isJump, Boolean countersign, Boolean signBack, Object histVar) {
        super(taskId, prod, bean);
        this.assigneeUser = assigneeUser;
        this.target = target;
        this.comment = comment;
        this.sequentials = sequentials;
        this.isJump = isJump;
        this.countersign = countersign;
        this.signBack = signBack;
        this.histVar = histVar;
    }

    @Override
    protected Void execute(CommandContext commandContext, TaskEntity task) {
        if (!task.getAssignee().equals(Authentication.getAuthenticatedUser().getDeptId())) {
            throw new ActivitiObjectNotFoundException("Cannot find task with id " + task.getId(), Task.class);
        }
        commandContext.addAttribute(CommandContext.SUBMIT_COMMENT, comment);
        commandContext.addAttribute(CommandContext.COMMENT, comment);
        BpmnModel bpmnModel = commandContext.getProcessEngineConfiguration().getRepositoryService().getBpmnModel(task.getProcessDefinitionId());

        if (!(countersign || signBack)) {
            executeBeforeHandler(bpmnModel, task, CustomEvent.CustomEventEnum.TASK_SUBMIT_BEFORE, comment, OperatorEnum.TASK_SUBMIT_OPERATOR, assigneeUser);
        }

        //保存审批意见
        addTaskComment(taskId, task.getProcessInstanceId(), Comment.USER_COMMENT, comment);
        //定义瞬息变量
        Map<String, Object> nextTransientVariables = new HashMap<>();
        //查找下一个任务节点，然后吧节点的名称和指派人设置到nextTransientVariables里面
        FlowElement nextElement = findNextElement(commandContext, task);
        //如果是可跳跃 目标节点不一定是下个节点
        if (!isJump.equals("1")) {
            nextTransientVariables.put("outgoing", target);
        } else {
            nextTransientVariables.put("outgoing", "");
        }
        //没有找到下一个节点，不意味着不存在下一个节点，也有可能会存在多个输出，但是却没有指定输出
        if (nextElement != null && nextElement instanceof KaiteTask) {
            KaiteTask kaiteTask = (KaiteTask) nextElement;
            // 是kaiteTask，并且不是固定人节点，往瞬息变量里面添加指派人
            if (!(kaiteTask instanceof KaiteFixedUserTask || kaiteTask instanceof KaiteRandomUserTask
                    || kaiteTask instanceof KaiteMultiUserTask
                    || kaiteTask instanceof KaiteCounterSignUserTask || kaiteTask instanceof KaiteCandidateUserTask)) {
                if (StringUtils.isEmpty(assigneeUser)) {
                    throw new ActivitiException(String.format("Please specify the assign of the task %s.", task.getName()));
                }
            }
            if (!Strings.isNullOrEmpty(assigneeUser)) {
                setAssigneeUser(nextTransientVariables);
            }
        }

        if (nextElement != null && nextElement instanceof Gateway) {
            setAssigneeUser(nextTransientVariables);
        }
        //是否是跳跃
        if (isJump.equals("1")) {
            commandContext.addAttribute(CommandContext.IS_JUMP, isJump);
            commandContext.addAttribute(CommandContext.IS_JUMP_COMMENT, "1");
            String targetNodeId = "";
            List<FlowElement> flowElementList = bpmnModel.getMainProcess().getFlowElementList();
            for (FlowElement flowElement : flowElementList) {
                if (flowElement instanceof SequenceFlow) {
                    if (flowElement.getId().equals(target)) {
                        targetNodeId = ((SequenceFlow) flowElement).getTargetRef();
                    }
                }
            }
            //跳跃的目标节点
            commandContext.addAttribute(CommandContext.JUMP_TARGET_NODE_ID, targetNodeId);
        }

        if (variables != null && !variables.isEmpty()) {
            if (localScope) {
                task.setVariablesLocal(variables);
            } else if (task.getExecutionId() != null) {
                task.setExecutionVariables(variables);
            } else {
                task.setVariables(variables);
            }
        }

        if (transientVariables == null) {
            transientVariables = new HashMap<>();
        }
        transientVariables.putAll(nextTransientVariables);
        //当前节点
        //流程提交人id
        if (signBack || countersign) {
            if (histVar instanceof Map) {
                Map<String, Object> histVarMap = (Map<String, Object>) histVar;
                transientVariables.put(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_NAME, histVarMap.get(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_NAME));
                transientVariables.put(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_ID, histVarMap.get(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_ID));
                transientVariables.put(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_TARGET, histVarMap.get(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_TARGET));
                transientVariables.put(PREV_ASSIGNEE, histVarMap.get(KaiteBaseUserTaskActivityBehavior.PREV_ASSIGNEE));
                task.getExecution().setVariable(PREV_ASSIGNEE, histVarMap.get(KaiteBaseUserTaskActivityBehavior.PREV_ASSIGNEE));
            }
            if (countersign) {
                transientVariables.put(COUNTERSIGN_PREV_ASSIGNEE, Authentication.getAuthenticatedUser().getDeptId());
                transientVariables.put(KaiteBaseUserTaskActivityBehavior.COUNTERSIGN_DIRECT_TASK_NAME, task.getName());
                transientVariables.put(KaiteBaseUserTaskActivityBehavior.COUNTERSIGN_DIRECT_TASK_ID, task.getTaskDefinitionKey());
                transientVariables.put(KaiteBaseUserTaskActivityBehavior.COUNTERSIGN_DIRECT_TASK_TARGET, target);
            }
        } else {
            transientVariables.put(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_NAME, task.getName());
            transientVariables.put(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_ID, task.getTaskDefinitionKey());
            transientVariables.put(KaiteBaseUserTaskActivityBehavior.DIRECT_TASK_TARGET, target);
            transientVariables.put(KaiteBaseUserTaskActivityBehavior.SEQUENTIALS, sequentials);
            if (!(bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey()) instanceof KaiteMultiUserTask)) {
                transientVariables.put(PREV_ASSIGNEE, Authentication.getAuthenticatedUser().getDeptId());
                task.getExecution().setVariable(PREV_ASSIGNEE, Authentication.getAuthenticatedUser().getDeptId());
            }
        }
        if (countersign) {
            transientVariables.put(KaiteBaseUserTaskActivityBehavior.HIST_COUNTERSIGN, task.getVariables());
        }

        transientVariables.put(KaiteBaseUserTaskActivityBehavior.COUNTERSIGN, countersign);
        UpcomingCommentInfoDTO upcomingInfo = UpcomingCommentInfoDTO.build(SubmitTypeEnum.SUBMIT,
                commandContext.getBean(), task.getBusinessKey(),
                comment, task.getId(), null, assigneeUser);
        Context.getCommandContext().addAttribute(KaiteBaseUserTaskActivityBehavior.UPCOMINGINFO, upcomingInfo);
        //添加审批告知变量
        Context.getCommandContext().getProcessEngineConfiguration().getProcessInstanceHelper()
                .addApprovalNotificationVariable(bpmnModel, task.getTaskDefinitionKey(), comment, SubmitTypeEnum.SUBMIT.getName());

        if (transientVariables != null && !transientVariables.isEmpty()) {
            if (localScope) {
                task.setTransientVariablesLocal(transientVariables);
            } else {
                task.setTransientVariables(transientVariables);
            }
        }

        Map<String, Object> hiTaskVar = task.getVariablesLocal();
        hiTaskVar.remove("datas");
        setTaskVariables(hiTaskVar);
        //执行任务完成
        executeTaskComplete(commandContext, task, variables, localScope);
        Context.getCommandContext().addAttribute(KaiteBaseUserTaskActivityBehavior.UPCOMINGINFO, upcomingInfo);
        completeUpcomingInfo(upcomingInfo);

        if (!(countersign || signBack)) {
            executeAfterHandler(bpmnModel, task, CustomEvent.CustomEventEnum.TASK_SUBMIT_AFTER, comment, OperatorEnum.TASK_SUBMIT_OPERATOR, assigneeUser);
        }

        return null;
    }

    public void completeUpcomingInfo(UpcomingCommentInfoDTO upcomingInfo) {
        Context.getCommandContext().getProcessEngineConfiguration().getActivitiUpcomingRun().
                completeUpcoming(upcomingInfo);
    }

    /**
     * 查找下一个节点
     * 获取当前任务model，然后获取当前节点下面的顺序流 outgoingList，如果只有一个顺序流则返回，如果大于1或者拿到的顺序流不在指定的target里面
     * 返回null，如果 outgoingList 的引用节点 里面存在指定的target，则返回这个taskNode
     *
     * @param commandContext
     * @param task
     * @return
     */
    private FlowElement findNextElement(CommandContext commandContext, TaskEntity task) {
        RepositoryService repositoryService = commandContext.getProcessEngineConfiguration().getRepositoryService();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        FlowElement taskFlowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        if (taskFlowElement == null) {
            throw new ActivitiException(String.format("Can't find task %s definiton from the process.", task.getName()));
        }
        //如果target不是空，并且出线得地方和当前节点是自由节点，直接返回当前节点
//        if (!Strings.isNullOrEmpty(target) && taskFlowElement instanceof KaiteLoopUserTask) {
        if (!Strings.isNullOrEmpty(target)) {
            FlowElement flowElement = bpmnModel.getFlowElement(target);
            if (flowElement instanceof SequenceFlow) {
                SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
                if (sequenceFlow.getTargetFlowElement().getId().equals(taskFlowElement.getId())) {
                    return taskFlowElement;
                }
            }
        }
        KaiteTask kaiteTask = (KaiteTask) taskFlowElement;
        List<SequenceFlow> outgoingList = kaiteTask.getOutgoingFlows();
        if (outgoingList.isEmpty()) {
            return null;
        }

        if (outgoingList.size() == 1) {
            return bpmnModel.getFlowElement(outgoingList.get(0).getTargetRef());
        }

        // >1的情况
        if (StringUtils.isEmpty(target)) {
            return null;
        }

        for (SequenceFlow each : outgoingList) {
            if (each.getId().equals(target)) {
                return each.getTargetFlowElement();
            }
        }
        return null;
    }

    @Override
    protected String getSuspendedTaskException() {
        return "Cannot complete a suspended task";
    }

    public Map<String, Object> getTaskVariables() {
        return taskVariables;
    }

    private void setTaskVariables(Map<String, Object> taskVariables) {
        this.taskVariables = taskVariables;
    }

    private void setAssigneeUser(Map<String, Object> nextTransientVariables) {
        JSONArray assigneeUsers = JSONArray.parseArray(assigneeUser);
        if (assigneeUsers != null) {
            for (Object user : assigneeUsers) {
                JSONObject userJson = (JSONObject) JSONObject.parse(String.valueOf(user));
                nextTransientVariables.put(userJson.getString("nodeId"), userJson.getString("assignee"));
                nextTransientVariables.put(userJson.getString("nodeId"), userJson.getString("assignee"));
            }
        }
    }

}
